package com.huterox.todoscheduler.core.suports;


import com.huterox.todoscheduler.annotate.*;
import com.huterox.todoscheduler.common.BeanNameConvert;
import com.huterox.todoscheduler.core.enumType.TodoItemElementType;
import com.huterox.todoscheduler.core.enumType.TodoListElementType;
import com.huterox.todoscheduler.core.enumType.TodoListTempleCreateType;
import com.huterox.todoscheduler.core.global.TodoListTemplateMap;
import com.huterox.todoscheduler.core.wapper.BeanWrapper;
import com.huterox.todoscheduler.core.wapper.TodoItemMethodWrapper;
import com.huterox.todoscheduler.core.wapper.TodoListWrapper;
import com.huterox.todoscheduler.exception.TodoDuplicateDefinitionException;
import com.huterox.todoscheduler.exception.TodoItemMisMatchError;
import com.huterox.todoscheduler.exception.TodoMissingDefinitionException;
import com.huterox.todoscheduler.handler.TodoListAfterHandler;
import com.huterox.todoscheduler.handler.TodoListBeforeCreateHandler;
import com.huterox.todoscheduler.handler.TodoListErrorHandler;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * 解析出清单模板，然后的话，把这个清单模板放在我们的global
 * 的TodListTemplateMap当中，因为后面清单工厂和任务调度器都要用到这个玩意
 * 所以TodoListTemplateMap是作为全局变量使用的
 * */
public class TodoListTemplateContext implements Serializable {

    private final TodoApplicationContext todoApplicationContext;
    private final TodoListTemplateMap todoListTemplateMap;

    //统计有TodoList的数量和实际清单的数量是否相同，如果不相同不好意思，创建失败
    private final Map<String,Integer> ListFromTodoItemAn = new HashMap<>();

    public TodoListTemplateContext() {
        //拿到容器
        todoApplicationContext = new TodoApplicationContext();
        //拿到TodoListTemplateMap
        todoListTemplateMap = TodoListTemplateMap.getInstance();
        //创建清单
        doCreateTodoListTemplate();
        //是否为安全创建
        doCheckSafeCreate();
    }

    private void doCheckSafeCreate(){
        if(todoListTemplateMap.getSize()!=this.ListFromTodoItemAn.size()){
            new TodoMissingDefinitionException("Missing task list definition",-1)
                    .printStackTrace();
            System.exit(-1);
        }
    }

    private void doCreateTodoListTemplate() {
        for (Map.Entry<String, BeanWrapper> beanWrapperEntry : this.todoApplicationContext
                .getFactoryBeanInstanceCache().entrySet())
        {
            /*
             * 由于历史遗留问题，我们在创建IOC容器的时候，beanName和全包名都放在了
             * map当中，这里主要是为了getBean方法，通过class也可以调用导致的，也就是有通过
             * className.getName() 获取到容器，所以存了两个，并且这两个对象是不同的可以删掉，但是先
             * 留着也许那天用上了。
             * */
            BeanWrapper beanWrapper = beanWrapperEntry.getValue();
            Class<?> clazz = beanWrapper.getWrapperClass();
            String beanName = BeanNameConvert.toLowerFirstCase(clazz.getSimpleName());
            String willCreateBeanName = beanWrapperEntry.getKey();
            if(beanName.equals(willCreateBeanName)){
                analysisTemplate(beanWrapper);
            }
        }
    }

    /**
     * 解析在清单注解上面的处理器
     * */
    private TodoListWrapper parseTodoListHandler(Class<?> clazz,TodoListWrapper todoListWrapper){
        if(clazz.isAnnotationPresent(TodoListBeforeCreate.class)){
            TodoListBeforeCreate annotationTodoListBeforeCreate = clazz.getAnnotation(TodoListBeforeCreate.class);
            Class<TodoListBeforeCreateHandler> todoListBeforeCreateHandlerClass = annotationTodoListBeforeCreate.todoListBeforeCreate();
            try {
                TodoListBeforeCreateHandler todoListBeforeCreateHandler = todoListBeforeCreateHandlerClass.newInstance();
                todoListWrapper.setTodoListCreateCheckHandler(todoListBeforeCreateHandler);
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        if(clazz.isAnnotationPresent(TodoListAfter.class)){
            TodoListAfter annotationTodoListAfter = clazz.getAnnotation(TodoListAfter.class);
            Class<TodoListAfterHandler> todoListAfterHandlerClass = annotationTodoListAfter.todoListAfterHandler();
            TodoListAfterHandler todoListAfterHandler = null;
            try {
                todoListAfterHandler = todoListAfterHandlerClass.newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
            todoListWrapper.setTodoListAfterHandler(todoListAfterHandler);
        }
        if(clazz.isAnnotationPresent(TodoListError.class)){
            TodoListError annotationTodoListError = clazz.getAnnotation(TodoListError.class);
            Class<TodoListErrorHandler> todoListErrorHandlerClass = annotationTodoListError.todoListError();
            TodoListErrorHandler todoListErrorHandler = null;
            try {
                todoListErrorHandler = todoListErrorHandlerClass.newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
            todoListWrapper.setTodoListErrorHandler(todoListErrorHandler);
        }
        return todoListWrapper;
    }

    /**
     * 负责解析清单了，先解析出有哪些些清单
     * 注意由于这里支持两种模式创建清单：
     *  1. 像RabbitMQ一样直接在类上面创建
     *  2. 直接通过配置类创建
     * */

    private void analysisTemplate(BeanWrapper beanWrapper) {
        Class<?> clazz = beanWrapper.getWrapperClass();
        String beanName = BeanNameConvert.toLowerFirstCase(clazz.getSimpleName());

        //先解析这个TodoList类生成标签
        if(clazz.isAnnotationPresent(TodoList.class)){
            //发现是TodoList注解的类，于是先初始化生成
            TodoList todoListAn = clazz.getAnnotation(TodoList.class);
            String todoListName = todoListAn.TodoListName();
            TodoListElementType todoListElementType = todoListAn.TodoType();
            if("".equals(todoListName)){
                //如果没有写清单的名字，那么把当前的beanName作为清单的名字
                todoListName = beanName;
            }
            //获取到清单的名字
            if(todoListTemplateMap.containKey(todoListName)){
                TodoListWrapper todoListWrapper = todoListTemplateMap.get(todoListName);
                if(todoListWrapper.getCreateType()==TodoListTempleCreateType.UserCreateByCode){
                    //用户以及用代码创建了这个玩意，现在用注解又创建了一个是不允许的
                    new TodoDuplicateDefinitionException("There is a duplicate task list in the configuration",-1)
                            .printStackTrace();
                    System.exit(-1);
                } else if (todoListWrapper.getCreateType()==TodoListTempleCreateType.TodoListAnnotate) {
                    new TodoDuplicateDefinitionException("There is a duplicate task list in the annotation",-1)
                            .printStackTrace();
                    System.exit(-1);
                }else if (todoListWrapper.getCreateType()==TodoListTempleCreateType.TodoItemEarly){
                    //如果是提取创建的，将对应的信息进行修正
                    todoListWrapper.setTodoListElementType(todoListElementType);
                    parseTodoListHandler(clazz, todoListWrapper);
                }
            }else {
                TodoListWrapper todoListWrapper = new TodoListWrapper();
                todoListWrapper.setTodoListName(todoListName);
                todoListWrapper.setTodoListElementType(todoListElementType);
                todoListWrapper.setCreateType(TodoListTempleCreateType.TodoListAnnotate);
                //获取对应的处理器
                parseTodoListHandler(clazz, todoListWrapper);
                todoListTemplateMap.put(todoListName,todoListWrapper);
            }
        }
        //解析方法，把方法加入进来
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for(Method method:declaredMethods){
            method.setAccessible(true);
            if(!(method.isAnnotationPresent(TodoItem.class))) continue;
            TodoItem todoItemAn = method.getAnnotation(TodoItem.class);

            //获取到这个家伙对应的清单信息
            String[] todoListNames = todoItemAn.TodoListNames();
            int[] itemIdxes = todoItemAn.TodoListItemIndex();
            TodoItemElementType[] todoItemElementTypes = todoItemAn.TodoItemType();
            if(!(todoListNames.length == itemIdxes.length && todoListNames.length == todoItemElementTypes.length)){
                new TodoItemMisMatchError("任务项数量与清单数量不匹配",-1).printStackTrace();
                System.exit(-1);
            }
            //将当前的方法，加入到对应的清单当中
            int indexM = 0;
            for(String todoListName:todoListNames){
                //进行一个简单记录，在方法当中清单的数量和实际通过配置以及TodoList注解创建的
                //清单数量是不是匹配的，因为存在，清单没有先初始化，但是方法先扫描到的情况。
                //所以为了安全，我们还是会选择先创建任务清单模板。最后在比对一下有没有对上数量
                //如果没有对上数量，不好意思，非法创建
                this.ListFromTodoItemAn.put(todoListName,1);
                TodoListWrapper todoListWrapper = null;
                //1. 先判断当前有没有这个清单，如果没有进行创建
                if(todoListTemplateMap.containKey(todoListName)){
                    todoListWrapper = todoListTemplateMap.get(todoListName);
                }else {
                    todoListWrapper = new TodoListWrapper();
                    todoListWrapper.setTodoListName(todoListName);
                    //这个信息暂时不知道，先默认生成先，后面等检测到了TodoList注解对应的真正的
                    //定义好了的信息，再修正就好了
                    todoListWrapper.setTodoListElementType(TodoListElementType.StrongConsistency);
                    todoListWrapper.setCreateType(TodoListTempleCreateType.TodoListAnnotate);
                    todoListTemplateMap.put(todoListName,todoListWrapper);
                }
                //把这个家伙对应的执行方法放进去
                Map<Integer, TodoItemMethodWrapper> itemMethodMap = todoListWrapper.getItemMethodMap();
                TodoItemMethodWrapper todoItemMethodWrapper = new TodoItemMethodWrapper();
                todoItemMethodWrapper.setWrapperMethod(method);
                todoItemMethodWrapper.setIdx(indexM);
                todoItemMethodWrapper.setOrder(itemIdxes[indexM]);
                todoItemMethodWrapper.setWrapperClass(clazz);
                todoItemMethodWrapper.setWrapperInstance(beanWrapper.getWrapperInstance());
                todoItemMethodWrapper.setTodoItemElementType(todoItemElementTypes[indexM]);
                itemMethodMap.put(itemIdxes[indexM],todoItemMethodWrapper);
                indexM++;
            }
        }
    }

}
